package com.ruoyi.system.service.h5.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.vo.h5.FinancialRankVO;
import com.ruoyi.system.domain.vo.h5.IncreaseRankingVO;
import com.ruoyi.system.domain.vo.h5.SubdivideSubjectVO;
import com.ruoyi.system.service.*;
import com.ruoyi.system.service.h5.IAllSubjectService;
import com.ruoyi.system.service.h5.MiniDailyReplayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author :liding
 * @date : 2023/11/16 10:49
 */
@Service
public class AllSubjectServiceImpl implements IAllSubjectService {
    @Autowired
    private IMiniSubjectService miniSubjectService;
    @Autowired
    private IMiniStockService miniStockService;
    @Autowired
    private MiniSubjectHistoryService miniSubjectHistoryService;
    @Autowired
    private MiniDailyReplayService miniDailyReplayService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ApiProfitabilityService apiProfitabilityService;
    @Autowired
    private ApiScoreReportService apiScoreReportService;
    @Autowired
    private ApiDebtPayAbilityService apiDebtPayAbilityService;
    @Autowired
    private ApiFinancialKpiService apiFinancialKpiService;
    @Autowired
    private ApiProfitSegmentationService apiProfitSegmentationService;
    @Autowired
    private ApiAchievementsReportService apiAchievementsReportService;
    @Autowired
    private MiniStockSubjectService miniStockSubjectService;
    @Autowired
    private ApiMoneyInflowTrendService apiMoneyInflowTrendService;
    @Autowired
    private ApiStageMaxMinService apiStageMaxMinService;
    /**
     * 题材列表
     *
     * @return
     */
    @Override
    public AjaxResult<List<MiniSubject>> list(int level) {
        List<MiniSubject> list = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery()
                .eq(MiniSubject::getLevel, level)
                .eq(MiniSubject::getDelFlag, 0)
                .eq(MiniSubject::getStatus,0)
                .gt(MiniSubject::getStockCount, 0)
                .orderByDesc(MiniSubject::getRate));
        return AjaxResult.success(list);
    }

    /**
     * 子题材
     *
     * @param parentId
     * @return
     */
    @Override
    public AjaxResult<List<MiniSubject>> childTree(Long parentId) {
        List<MiniSubject> list = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery()
                .eq(MiniSubject::getParentId, parentId)
                .eq(MiniSubject::getDelFlag, 0)
                .eq(MiniSubject::getStatus,0)
                .orderByAsc(MiniSubject::getOrderNum));
        return AjaxResult.success(list);
    }

    /**
     * 点击题材进入题材排名
     *
     * @param parentId
     * @return
     */
    @Override
    public AjaxResult<MiniSubject> childStockTree(Long parentId) {

        MiniSubject clickedSubject = miniSubjectService.getById(parentId);
        List<MiniSubject> list = new ArrayList<>();
        list.add(clickedSubject);
        getChildrenList(list);
        return AjaxResult.success(clickedSubject);
    }

    private void getChildrenList(List<MiniSubject> childrenList) {
        List<Long> levelTwoIds = childrenList.stream().map(MiniSubject::getSubjectId).collect(Collectors.toList());

        List<MiniSubject> levelThreeList = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery()
                .in(MiniSubject::getParentId, levelTwoIds)
                .eq(MiniSubject::getDelFlag, 0)
                .eq(MiniSubject::getStatus,0));
        if (CollectionUtils.isEmpty(levelThreeList)){
            List<MiniStock> stockList = miniStockService.getMiniStockListBySubjectIds(levelTwoIds);
            Map<String, List<MiniStock>> stockMap = stockList.stream().collect(Collectors.groupingBy(MiniStock::getSubjectIds));
            for (MiniSubject c : childrenList) {
                c.setStocks(stockMap.get(c.getSubjectId().toString()));
            }
            return;
        }
        Map<Long, List<MiniSubject>> levelThreeMap = levelThreeList.stream().collect(Collectors.groupingBy(MiniSubject::getParentId));
        for (MiniSubject c : childrenList) {
            List<MiniSubject> subjects = levelThreeMap.get(c.getSubjectId());
            c.setChildren(subjects);
            if (CollectionUtils.isEmpty(subjects)){
                List<Long> subjectIds = new ArrayList<>();
                subjectIds.add(c.getSubjectId());
                List<MiniStock> stockList = miniStockService.getMiniStockListBySubjectIds(subjectIds);
                c.setStocks(stockList);
            }
        }
        getChildrenList(levelThreeList);
    }

    /**
     * 细分题材
     *
     * @return
     */
    @Override
    public AjaxResult<SubdivideSubjectVO> subdivideSubject() {
        List<MiniSubject> level1 = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery()
                .eq(MiniSubject::getLevel, 1)
                .eq(MiniSubject::getDelFlag, 0)
                .eq(MiniSubject::getStatus,0)
                .orderByDesc(MiniSubject::getRate));
        List<MiniSubject> level2 = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery()
                .eq(MiniSubject::getLevel, 2)
                .eq(MiniSubject::getDelFlag, 0)
                .eq(MiniSubject::getStatus,0)
                .orderByDesc(MiniSubject::getRate));
        List<MiniSubject> level3 = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery()
                .eq(MiniSubject::getLevel, 3)
                .eq(MiniSubject::getDelFlag, 0)
                .eq(MiniSubject::getStatus,0)
                .orderByDesc(MiniSubject::getRate));
        return AjaxResult.success(new SubdivideSubjectVO(level1,level2,level3));
    }

    @Override
    public AjaxResult<Map<String, List<MiniSubjectHistory>>> subjectCircle(String startDate, String endDate, String type) {
        LambdaQueryWrapper<MiniSubjectHistory> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(MiniSubjectHistory::getHistoryDay, startDate, endDate)
                .eq(MiniSubjectHistory::getLevel,1);
        if ("1".equals(type)){
            wrapper.orderByDesc(MiniSubjectHistory::getStockRiseCount);
        } else if ("3".equals(type)) {
            wrapper.orderByDesc(MiniSubjectHistory::getRate);
        } else if ("2".equals(type)) {
            wrapper.orderByDesc(MiniSubjectHistory::getMoneyInflow);
        }
        List<MiniSubjectHistory> list = miniSubjectHistoryService.list(wrapper);
        Map<String, List<MiniSubjectHistory>> collect = list.stream().filter(it->it.getStockCount()!=null&& it.getStockCount()>0).collect(Collectors.groupingBy(it->it.getHistoryDay().format(DateTimeFormatter.ISO_DATE)));
        Map<String, List<MiniSubjectHistory>> sortedMap = new LinkedHashMap<>();
        collect.entrySet().stream()
                .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                .forEachOrdered(entry -> sortedMap.put(entry.getKey(), entry.getValue()));

        return AjaxResult.success(sortedMap);
    }

    @Override
    public AjaxResult dailyReplay(String day) {
        List<MiniDailyReplay> list = miniDailyReplayService.list(Wrappers.<MiniDailyReplay>lambdaQuery()
                .eq(MiniDailyReplay::getReplayDay, day)
                .orderByDesc(MiniDailyReplay::getId));
        if (CollectionUtil.isEmpty(list)){
            //沪深基础数据API文档--涨停股池
            MiniDailyReplay[] result = null;
            try {
                String url = String.format("http://api.mairuiapi.com/hslt/ztgc/%s/%s", day, Constants.STOCK_API_KEY);
                result = restTemplate.getForObject(url, MiniDailyReplay[].class);
            } catch (RestClientException e) {
                return AjaxResult.error("获取远程信息失败");
            }
            if (result == null){
                return AjaxResult.error("获取股票失败");
            }
            list = Arrays.asList(result);
            list.forEach(it->{
                it.setReplayDay(day);
                it.setDm(it.getDm().replace("sz", "").replace("sh",""));
            });
            miniDailyReplayService.saveBatch(list);
        }
        Map<Integer, List<MiniDailyReplay>> collect = list.stream().collect(Collectors.groupingBy(MiniDailyReplay::getLbc));
        LinkedHashMap<Integer, List<MiniDailyReplay>> collect1 = collect.entrySet().stream().sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                .collect(LinkedHashMap::new,
                        (map, entry) -> map.put(entry.getKey(), entry.getValue()),
                        LinkedHashMap::putAll);
        return AjaxResult.success(collect1);
    }

    @Override
    public AjaxResult addedStock(String day) {
        List<MiniStock> stocks = miniStockService.selectMiniStockByDay(day);
        Map<String, List<MiniStock>> collect = stocks.stream().collect(Collectors.groupingBy(MiniStock::getSubjectName));
        return AjaxResult.success(collect);
    }

    /**
     * 财务排名
     *
     * @param subjectId
     * @param year
     * @param quarter
     * @param type
     * @return
     */
    @Override
    public AjaxResult financialRank(Long subjectId, String year, String quarter, String type) {
        List<MiniStock> stockList = miniStockService.selectStockBySubject(subjectId);
        Map<String, MiniStock> stockNoMap = stockList.stream().collect(Collectors.toMap(MiniStock::getStockNo, Function.identity(), (key1, key2) -> key2));
        List<String> stockNoList = stockList.stream().map(MiniStock::getStockNo).collect(Collectors.toList());
        if (CollUtil.isEmpty(stockNoList)) {
            return AjaxResult.success(new ArrayList<>());
        }
        List<FinancialRankVO> result = null;
        //营业总收入 销售毛利率 销售净利率
        if ("income".equals(type)||"saleGrossMargin".equals(type)||"saleNetProfitMargin".equals(type)){
            result = apiProfitabilityService.getByYearAndQuarterAndStockList(stockNoList,year,quarter);
            String yesterday = DateUtil.yesterday().toDateStr();
            List<ApiMoneyInflowTrend> list = apiMoneyInflowTrendService.list(Wrappers.<ApiMoneyInflowTrend>lambdaQuery().eq(ApiMoneyInflowTrend::getT, yesterday)
                    .in(ApiMoneyInflowTrend::getStockNo, stockNoList));
            if (CollUtil.isNotEmpty(list)){
                Map<String, ApiMoneyInflowTrend> map = list.stream().collect(Collectors.toMap(ApiMoneyInflowTrend::getStockNo, Function.identity(), (k1, k2) -> k2));
                result.forEach(it-> it.setMoneyInflowTrend(map.get(it.getStockNo())));
            }
        }
        /*else if ("incomeRise".equals(type)){
            result = apiScoreReportService.getIncomeRiseByYearAndQuarterAndStockList(stockNoList,year,quarter);
        }*/
        else if ("noProfit".equals(type)){
            result = dealNoProfit(stockList,year,quarter);
        }
        /*else if ("noProfitRise".equals(type)){
            result = new ArrayList<>();
        }*/
        else if ("assetLiabilityRate".equals(type)||"incomeRise".equals(type)||"noProfitRise".equals(type)) {
            // 资产负债率
            // result = apiDebtPayAbilityService.getAsseteByYearAndQuarterAndStockList(stockNoList,year,quarter);
            result = apiAchievementsReportService.list(Wrappers.<ApiAchievementsReport>lambdaQuery()
                    .eq(ApiAchievementsReport::getY, year)
                    .eq(ApiAchievementsReport::getQ, quarter)
                    .in(ApiAchievementsReport::getDm, stockNoList))
                    .stream().map(ApiAchievementsReport::toFinancialRankVO).collect(Collectors.toList());
        } else{
            result = new ArrayList<>();
        }
        for (FinancialRankVO financialRankVO : result) {
            MiniStock miniStock = stockNoMap.get(financialRankVO.getStockNo());
            if (ObjectUtil.isNotNull(miniStock)){
                financialRankVO.setStockRise(miniStock.getRise());
                financialRankVO.setStockName(miniStock.getName());
                financialRankVO.setStockNo(miniStock.getStockNo());
                financialRankVO.setFlowMarketValue(miniStock.getFlowMarketValue());
                financialRankVO.setTotalMarketValue(miniStock.getTotalMarketValue());
                financialRankVO.setPriceEarningsRatio(miniStock.getPriceEarningsRatio());
            }
        }
        return AjaxResult.success(result);
    }

    /**
     * 根据股票代码查题材图谱
     *
     * @param stockNo
     * @return
     */
    @Override
    public AjaxResult<MiniSubject> getSubjectTree(String stockNo) {
        MiniStock stock = new MiniStock();
        stock.setStockNo(stockNo);
        List<MiniStock> stockList = miniStockService.selectMiniStockList(stock);
        if (CollUtil.isEmpty(stockList)){
            return AjaxResult.success();
        }
        String[] split = stockList.get(0).getAncestors().split(",");

        return childStockTree(Long.valueOf(split[2]));
    }

    /**
     * 利润表
     *
     * @param stockNo
     * @param quarter
     * @return
     */
    @Override
    public AjaxResult profitChart(String stockNo, String quarter) {
        List<ApiScoreReport> list = apiScoreReportService.list(Wrappers.<ApiScoreReport>lambdaQuery().eq(ApiScoreReport::getDm, stockNo)
                .eq(ApiScoreReport::getQ,quarter));
        List<String> title = new ArrayList<>();
        List<BigDecimal> profit = new ArrayList<>();
        List<BigDecimal> profitRate = new ArrayList<>();
        List<BigDecimal> income = new ArrayList<>();
        List<BigDecimal> incomeRate = new ArrayList<>();
        for (ApiScoreReport t : list) {
            title.add(t.getYq());
            profit.add(t.getJlr());
            profitRate.add(t.getJlrtb());
            income.add(t.getYysr());
            incomeRate.add(t.getYysrtb());
        }
        LinkedHashMap<String, Object> results =  new LinkedHashMap<>();
        results.put("title",title);
        results.put("profit",profit);
        results.put("profitRate",profitRate);
        results.put("income",income);
        results.put("incomeRate",incomeRate);
        return AjaxResult.success(results);
    }

    @Override
    public AjaxResult industryPie(String stockNo) {
        List<ApiProfitSegmentation> list = apiProfitSegmentationService.list(Wrappers.<ApiProfitSegmentation>lambdaQuery().eq(ApiProfitSegmentation::getDm, stockNo));
        return AjaxResult.success(list);
    }
    /**
     * 情绪排名
     * @param subjectId
     * @param day
     * @return
     */
    @Override
    public AjaxResult emotionRank(Long subjectId, Integer day) {
        List<MiniDailyReplay> list = miniStockSubjectService.selectStockBySubjectAncestors(subjectId);
        /*if (CollUtil.isEmpty(list)){
            return AjaxResult.success(new ArrayList<>());
        }
        List<String> stockNoList = list.stream().map(MiniStockSubject::getStockNo).collect(Collectors.toList());
        // List<String> dayList = miniDailyReplayService.selectTradingDay(day);
        // List<MiniStockSubject> result = miniDailyReplayService.selectEmotionRank(stockNoList,dayList);
        for (String no : stockNoList) {
            String url = String.format("http://api.mairuiapi.com/hszbl/fsjy/%s/%s/%s", no,"dn", Constants.STOCK_API_KEY);
            ApiHistoryStockInfo[] result = restTemplate.getForObject(url, ApiHistoryStockInfo[].class);

        }*/
        return AjaxResult.success(list);
    }

    @Override
    public List<IncreaseRankingVO> increaseRanking(String day, Long subjectId) {
        if (StringUtils.isBlank(day) || LocalDate.now().equals(LocalDate.parse(day))){
            day = getLastWorkday(LocalDate.now()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
        // List<IncreaseRankingVO> result = apiStageMaxMinService.selectIncreaseRanking(day);
        List<IncreaseRankingVO> result = apiStageMaxMinService.selectIncreaseRankingBySubjectId(day,subjectId);
        return result;
    }
    public static LocalDate getLastWorkday(LocalDate date) {
        LocalDate previousDay = date.minusDays(1); // 获取前一天日期

        // 判断前一天是否是工作日（即不是周六或周日）
        if (previousDay.getDayOfWeek() != DayOfWeek.SATURDAY && previousDay.getDayOfWeek() != DayOfWeek.SUNDAY) {
            return previousDay;
        }

        // 如果前一天是周末，继续减去天数直到找到一个工作日
        return previousDay.minusDays(previousDay.getDayOfWeek() == DayOfWeek.SUNDAY ? 2 : 1);
    }
    @Override
    public AjaxResult<List<MiniSubject>> stockSubjectList(String stockNo) {
        MiniStock stock = new MiniStock();
        stock.setStockNo(stockNo);
        List<MiniStock> stockList = miniStockService.selectMiniStockList(stock);
        Set<Long> subjectIds = new HashSet<>();
        stockList.forEach(x->{
            for (String s : x.getAncestors().split(",")) {
                subjectIds.add(Long.valueOf(s));
            }
            for (String s : x.getSubjectIds().split(",")) {
                subjectIds.add(Long.valueOf(s));
            }
        });
        subjectIds.removeIf(x->x==0||x==1);
        if (CollUtil.isEmpty(subjectIds)){
            return AjaxResult.success(new ArrayList<>());
        }
        List<MiniSubject> list = miniSubjectService.list(Wrappers.<MiniSubject>lambdaQuery().in(MiniSubject::getSubjectId, subjectIds));
        return AjaxResult.success(list);
    }

    private List<FinancialRankVO> dealNoProfit(List<MiniStock> stockList, String year, String quarter){
        List<String> stockNoList = stockList.stream().map(MiniStock::getStockNo).collect(Collectors.toList());
        List<ApiFinancialKpi> list = apiFinancialKpiService.list(Wrappers.<ApiFinancialKpi>lambdaQuery()
                .eq(ApiFinancialKpi::getY, year)
                .eq(ApiFinancialKpi::getQ, quarter)
                .in(ApiFinancialKpi::getStockNo, stockNoList));
        List<String> dbStockNo = list.stream().map(ApiFinancialKpi::getStockNo).collect(Collectors.toList());
        stockNoList.removeIf(x->dbStockNo.contains(x));

        List<ApiFinancialKpi> insertList = new ArrayList<>();
        for (String no : stockNoList) {
            String url = String.format("http://api.mairuiapi.com/hscp/cwzb/%s/%s", no, Constants.STOCK_API_KEY);
            ApiFinancialKpi[] result = restTemplate.getForObject(url, ApiFinancialKpi[].class);
            for (ApiFinancialKpi kpi : result) {
                String[] split = kpi.getDate().split("-");
                String insertYear = split[0];
                String insertQuarter = monthToQuarter(split[1]);
                insertList.add(new ApiFinancialKpi(kpi.getDate(),no ,null,kpi.getKflr(),insertYear,insertQuarter));
            }
            try {
                //API calls quota exceeded! maximum admitted 10 per Second.
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        apiFinancialKpiService.saveBatch(insertList);
        list.addAll(insertList.stream().filter(x->year.equals(x.getY())&&quarter.equals(x.getQ())).collect(Collectors.toList()));
        List<FinancialRankVO> result = new ArrayList<>();
        list.forEach(x->{
            FinancialRankVO vo = new FinancialRankVO();
            vo.setStockNo(x.getStockNo());
            vo.setNoProfit(x.getKflr());
            result.add(vo);
        });
        return result;
    }

    private String monthToQuarter(String month) {
        if ("03".equals(month)) {
            return "1";
        } else if ("06".equals(month)) {
            return "2";
        } else if ("09".equals(month)) {
            return "3";
        } else if ("12".equals(month)) {
            return "4";
        } else {
            return "";
        }
    }
}
